#region The MIT License
// The MIT License
// 
// Copyright (c) 2011 octalforty studios
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#endregion
using System;
using System.Diagnostics;
using System.IO;

using octalforty.Staytuned.ObjectModel;

using PostmarkDotNet;
using PostmarkDotNet.Model;

namespace octalforty.Staytuned.Impl.ComponentModel.Postmark
{
    /// <summary>
    /// Provides support for sending notification messages through Postmark.
    /// </summary>
    /// <remarks>
    /// <see cref="PostmarkNotificationSupportModule"/> supports publishing of <see cref="PostmarkMessage"/> via
    /// <c>email</c> scheme.
    /// </remarks>
    public class PostmarkNotificationSupportModule : NotificationSupportModule
    {
        #region Private Members
        private readonly string apiKey;
        private readonly string from;
        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="PostmarkNotificationSupportModule"/>.
        /// </summary>
        /// <param name="from"></param>
        /// <param name="apiKey"></param>
        public PostmarkNotificationSupportModule(string from, string apiKey) :
            base(new[] { "email" })
        {
            this.apiKey = apiKey;
            this.from = from;
        }

        public override void PublishMessage(Message message, string address)
        {
            var postmarkMessage = new PostmarkMessage {
                From = GetFrom(message),
                To = address,
                Subject = message.Subject,
                TextBody = message.Body,
            };

            if(message.ReplyTo != null)
                postmarkMessage.ReplyTo = message.ReplyTo.EffectiveAddress;

            foreach(var header in message.Headers)
                postmarkMessage.Headers[header.Key] = header.Value;

            foreach (var attachment in message.Attachments)
                using(var stream = attachment.ContentStreamProvider())
                    postmarkMessage.Attachments.Add(new PostmarkMessageAttachment {
                        Name = attachment.Name,
                        ContentType = attachment.ContentType,
                        Content = Convert.ToBase64String(ReadStream(stream, 8192))
                    });

            try
            {
                var postmarkClient = new PostmarkClient(apiKey);
                postmarkClient.SendMessage(postmarkMessage);
            } // try
            catch(Exception e)
            {
                Log.Error("could not publish Postmark message to '{0}': {1}", address, e);
                throw;
            } // catch
        }

        private string GetFrom(Message message)
        {
            if(message.Sender == null || string.IsNullOrEmpty(message.Sender.Name))
                return from;

            if(message.Sender.Address == null || string.IsNullOrEmpty(message.Sender.Address.EffectiveAddress))
                return string.Format("{0} <{1}>", message.Sender.Name, from);

            return string.Format("{0} <{1}>", message.Sender.Name, message.Sender.Address.EffectiveAddress);
        }

        private static byte[] ReadStream(Stream input, int bufferSize)
        {
            var buffer = new byte[bufferSize];
            
            using(var ms = new MemoryStream())
            {
                int read;
                while((read = input.Read(buffer, 0, buffer.Length)) > 0)
                    ms.Write(buffer, 0, read);

                return ms.ToArray();
            }
        }
    }
}
